<?php
// +-------------------------------------------------------------------
// | 
// +-------------------------------------------------------------------
// | Copyright (c) 2009-2016 All rights reserved.
// +-------------------------------------------------------------------
namespace Kcdns\Service\Cron;

/**
 * 计划任务守护进程
 */
class Cron
{
    
    // $tasks = [['cron' => '* * * * *', 'name' => '_log','param'=>'string']];
    protected static $tasks = array();

    protected static $task;

    protected static $crons;

    protected static $authKeyFile = "./Data/Cron/auth_key";

    protected static $deamonLoop = 0;

    protected static $cliCommand = '';

    /**
     * *******************************************************************************************
     * ***************************************** 启动入口 *****************************************
     * *******************************************************************************************
     */
    public static function start ($cliCommand)
    {
        self::$cliCommand = $cliCommand;
        if (self::$cliCommand)
        {
            isset($GLOBALS['argv'][2]) ? self::run($GLOBALS['argv'][2]) : self::_deamon();
        }
        else
        {
            IS_POST ? self::_worker() : self::_deamon();
        }
    }
    
    // 执行任务
    public static function run ($idOrName)
    {
        $task = Model::getInstance()->get($idOrName);
        if ($task)
        {
            $_POST = array(
                    'authKey' => self::getAuthKey(),
                    'task' => $task
            );
            self::_worker();
        }
    }

    public static function stop ($tasks)
    {
        unlink('./Data/Cron/cron.deamon.lock');
    }

    public static function status ()
    {
        $_status = Autorun::status();
        $status = array();
        foreach ($_status as $k => $v)
        {
            $status[substr($k, 5)] = $v;
        }
        return $status;
    }

    /**
     * *******************************************************************************************
     * ***************************************** 守护进程 *****************************************
     * *******************************************************************************************
     */
    protected static function _deamon ()
    {
        Autorun::start(array(
                'name' => 'cron.deamon',
                'interval' => 15,
                'maxloop' => 1000,
                'callback' => array(
                        __CLASS__,
                        'deamonInvoke'
                )
        ));
    }

    /**
     * 激活计划任务进程
     */
    public static function deamonInvoke ()
    {
        // POST 到当前地址激活计划任务
        $url = U('', '', true, true);
        $status = self::status();
        $tasks = Model::getInstance()->gets();
        $now = time();
        $info = array();
        
        // 没有可执行的任务, 退出
        $tasks or exit();
        
        foreach ($tasks as $_name => $_task)
        {
            // 初始化执行计划
            if (! self::$crons[$_name])
            {
                self::$crons[$_name]['expression'] = CronExpression::factory($_task['cron']);
                self::$crons[$_name]['next'] = self::$crons[$_name]['expression']->getNextRunDate()->getTimestamp();
            }
            
            $info[$_name] = date('H:i:s', self::$crons[$_name]['next']);
            
            // 未到达计划执行时间, 或者任务计划定义错误, 不执行
            if ($now < self::$crons[$_name]['next'] || ! self::$crons[$_name]['next'])
            {
                continue;
            }
            
            // 执行前更新计划时间
            self::$crons[$_name]['next'] = self::$crons[$_name]['expression']->getNextRunDate()->getTimestamp();
            
            // 任务正在执行
            if ($status[$_name])
            {
                continue;
            }
            
            if (self::$cliCommand)
            {
                exec(self::$cliCommand . " {$GLOBALS['argv'][0]} {$GLOBALS['argv'][1]} $_name > /dev/null 2>&1 &");
            }
            else
            {
                $option['TIMEOUT'] = 1;
                $data = array(
                        'authKey' => self::getAuthKey(),
                        'task' => $_task
                );
                o('util')->curl($url, $data, 'post', $option);
            }
        }
        
        // 执行 1000 次循环后重新启动
        // if (self::$deamonLoop ++ > 1000) { Autorun::clear(); o('util')->curl($url, '', false, $option); }
        
        return json_encode($info, JSON_UNESCAPED_UNICODE);
    }

    /**
     * *******************************************************************************************
     * ***************************************** 任务进程 *****************************************
     * *******************************************************************************************
     */
    protected static function _worker ()
    {
        if (! self::checkAuthKey($_POST['authKey']))
        {
            throw new \Exception("Invalid worker authKey : " . $_POST['authKey']);
        }
        
        self::$task = $_POST['task'];
        
        Autorun::start(array(
                'name' => 'cron.' . self::$task['name'] . '.' . self::$task['id'],
                'interval' => 1,
                'maxloop' => 1,
                'callback' => array(
                        __CLASS__,
                        'workerInvoke'
                )
        ));
    }

    /**
     * 执行计划任务
     */
    public static function workerInvoke ()
    {

        $className= "\\Cron\\Common\\" . self::$task['name'];
        $className= class_exists($className)?$className:"\\Kcdns".$className;
        $callback = array(
            $className,
                'run'
        );
        $startTime = microtime(true);
        $response = call_user_func($callback, self::$task['param']);
        $execTime = number_format(microtime(true) - $startTime, 3);
        
        Model::getInstance()->log(self::$task['name'], $execTime, $response);
        
        return "WORKER " . self::$task['name'] . ' / ' . $response;
    }

    /**
     * *******************************************************************************************
     * ***************************************** WEB 认证 *****************************************
     * *******************************************************************************************
     */
    protected static function getAuthKey ()
    {
        $authKey = file_get_contents(self::$authKeyFile) ?  : md5(uniqid());
        file_put_contents(self::$authKeyFile, $authKey);
        return $authKey;
    }

    /**
     * 验证授权码
     * 用于任务进程启动时检查请求来源是否合法
     */
    protected static function checkAuthKey ($authKey)
    {
        return $authKey == self::getAuthKey();
    }
}